Skip to content

fix(search): fan out only over values the leaf can read - #828

Open
ddeboer wants to merge 1 commit into
mainfrom
splittable-values
Open

fix(search): fan out only over values the leaf can read#828
ddeboer wants to merge 1 commit into
mainfrom
splittable-values

Conversation

@ddeboer

@ddeboer ddeboer commented Sep 4, 2026

Copy link
Copy Markdown
Member

Follow-up to #801, found while adapting Linked Open Limburg’s schema to ADR 26 against its real data.

The defect

A role is sometimes stated twice on one node – once as a string, once as a Wikidata entity. Beeld & Geluid does this, and LOL’s own schema comment noted the IRI “costs nothing and takes nothing away”, which was true while the entry held a list.

Under fan-out it stopped being true. tuplesOf split on every framed value, but a keyword leaf reads literals and passes over an IRI – so the IRI minted a second entry the role was absent from:

// role: ["fotograaf", <wikidata:Q33231>], one agent
[ { "role": "fotograaf", "creator": {}, "creator_id": "…/a/1" },
  {                      "creator": {}, "creator_id": "…/a/1" } ]

The same endpoint, apparently in no role at all – shown to a reader, and matched by a filter for that agent. Not a crash and not a hang: a phantom entry that reads as data.

Two things the shape happens to get right, worth stating so the fix is not over-drawn: keyword does drop the IRI, so no raw http://… is rendered as a role label, and applyIdentityCompanion dedupes, so the agent’s id appears once rather than twice.

The fix

The split follows the reader. splittable() narrows a leaf’s framed values to those its kind can read before the product is taken.

Only keyword needs it:

  • text has no filter operator (filterOperatorFor maps it to undefined), so nothing welds it and it is never a tuple position;
  • reference is one, and both of its shapes are meaningful – an IRI, and, for a local lookup, a node the graph named inline, which has no IRI and is a referent all the same.

Notes

  • This is the ninth defect the ADR 26 work has turned up, and the first found by pointing it at a real downstream schema rather than by review. The pattern from the earlier rounds holds: the fan-out itself is right, and what goes wrong is a step around it disagreeing with what the projection actually does.
  • Downstream: LOL’s schema change (codeberg limburg/lol, its issue 162) is what surfaced this. It needs @lde/search@0.25.0 plus this fix.

Found adapting a downstream schema to ADR 26, against its real data. A role is
sometimes stated twice on one node – as a string and as a Wikidata entity – and
a keyword leaf reads the literal and passes over the IRI. Fan-out split on both,
so the IRI minted a second entry the role was absent from: the same endpoint,
apparently in no role at all, shown to a reader and matched by a filter.

The split now follows the reader. Only `keyword` needs narrowing: a `text` field
has no filter operator, so nothing welds it and it is never a tuple position,
and a `reference` is one whose two shapes are both meaningful – an IRI, and a
node the graph named inline, which has no IRI and is a referent all the same.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant